# -*- coding: utf-8 -*-
#
# Name: Pyton Twisted binary file transfer demo (server)
# Description: Simple demo which shows how you can transfer binary files using
# the Twisted framework.
#
# Keep in mind that this is only a demo and there are many possible scenarios
# where program can break.
#
# Author: Tomaž Muraus (http://www.tomaz-muraus.info)
# License: GPL

# Requirements:
# - Python >= 2.5
# - Twisted (http://twistedmatrix.com/)

import os
import optparse
#import time
import simplejson as json
import logging
from twisted.internet import reactor, protocol
from twisted.protocols import basic
#import sys
from utils.common import COMMANDS, validate_file_md5_hash, get_file_md5_hash, read_bytes_from_file, clean_and_split_input

from TimeManager import Channel

ROOT_DIR = os.path.dirname(__file__)
ZIP_WIDGETS_FOLDER = os.path.join(ROOT_DIR, 'downloadedfiles')


# Terminal Constants
TELEVISION = 1
ANDROID_PHONE = 2
IPHONE = 3

log = logging.getLogger("AlicanteHomebox")

class FileTransferProtocol(basic.LineReceiver):
    delimiter = '\n'

    def connectionMade(self):
        self.factory.clients.append(self)      
        log.debug('Connection from: %s (%d clients total)' % (self.transport.getPeer().host, len(self.factory.clients)))
      
        self.file_handler = None
        self.file_data = ()
        self.listEvents = {}
        
        self.terminalType = None
        self.channelID = None
        self.screenSize = None
        self.videoPort = None
        # self.terminalID = None
        
        self.startedAt = None #time.time()
        self.timerinfo = reactor.callLater(10, self.timeout)
        self.getInfos()
        
        
    #def showInteraction(self, interaction_hash):
        #log.debug("Show interaction : %s" % interaction_hash)
        #msg = {'action' : 'showevent',
            #'hash' : interaction_hash}
            ## Envoie interaction id
        #self.transport.write( '%s\n' % json.dumps(msg) )        
        
    def hideInteraction(self, interaction_id):
        log.debug("Hide interaction : %s" % interaction_id)
        msg = {'action' : 'hideevent',
            'id' : interaction_id}
            # Envoie interaction id
        self.transport.write( '%s\n' % json.dumps(msg) )  
        
    def timeout(self):
        log.error('Can\'t to retreive infos from terminal')
        
    def getInfos(self):
        log.debug("Get info")
        # -> Terminal Type
        # -> The channelID
        # -> Started at
        # -> Screensize
        # -> The name of the logged guy?
        msg = {'action' : 'getinfos'}
        self.transport.write( '%s\n' % json.dumps(msg) )
          
    def setchannel(self, channelID):
        if channelID != self.channelID:
            if self.factory.videoCommunicator.addWatcher(channelID, self.transport.getPeer().host, self.videoPort):
                msg = { 'action' : 'play' }
                self.transport.write( '%s\n' % json.dumps(msg) )
            if not self.factory.channelsList.has_key(channelID):
                self.factory.channelsList[channelID] = Channel()
          
            if self.channelID:
                self.factory.channelsList[self.channelID].removeTerminal(self)
                
            self.factory.channelsList[channelID].addTerminal(self)
            self.channelID = channelID
            log.debug("Element added to TMD. Now %d terminal(s) watching %s" % ( len(self.factory.channelsList[self.channelID].terminals), self.channelID) )
            
            self.updateListEvents()
      
    def updateListEvents(self):
        # Send all widgets that the terminal should have
        self.listEvents = {}
        for event in self.factory.channelsList[self.channelID].eventsList.itervalues():
            if event.running():
                self.listEvents[(event.serviceID, event.version)] = {'hash':event.md5_hash, 'path':event.zipPath}
      
    def sendAvailableEvents(self):
        if self.listEvents.keys():
            log.debug("Send available event: %s" % self.listEvents.keys())
            msg = { 'action' : 'listevents',
                'value' : self.listEvents.keys(),
                'channel' : self.channelID }
            self.transport.write( '%s\n' % json.dumps(msg) )
        else:
            log.debug("No available event for the channel")
      
    def connectionLost(self, reason):
        if self.channelID:
            self.factory.channelsList[self.channelID].removeTerminal(self)
            
            
        self.factory.videoCommunicator.deleteWatcher(self.transport.getPeer().host, self.videoPort)
            
            
        self.factory.clients.remove(self)
        self.file_handler = None
        self.file_data = ()
        log.debug("Connection from %s lost (%d clients left)" % (self.transport.getPeer().host, len(self.factory.clients)))


    def lineReceived(self, line):
        #log.debug('Received the following line from the client [%s]: %s' % (self.transport.getPeer().host, line))
        log.debug("Received the line from the client")
        
        try:
            data = json.loads(line)
        except ValueError, e:
            log.error("Can't decode: %s" % e)
            return
            
        
        if len(data) == 0 or data == '':
            return 
        
        try:
            command = data['action'].upper()
        except (KeyError, ValueError), e:
            log.error("Can't decode: %s" % e)        

        #if not command in COMMANDS:
            #msg = {'action' : 'errormsg',
                #'value' : 'Invalid command'}
            #self.transport.write( '%s\n' % json.dumps(msg) )
            #return
        
        if command == 'SETINFOS':
            try:
                self.terminalType = data['terminaltype']
                self.startedat = data['startedat']
                self.screenSize = (int(data['screensize']['width']), int(data['screensize']['height']))
                self.videoPort = int(data['videoport'])
            except ValueError, e:
                log.debug('Can\'t decode %s' % e)
            else:
                self.timerinfo.cancel()
                try:
                    channel = int(data['channel'])
                except:
                    pass
                else:  
                    self.setchannel(channel)
                    self.sendAvailableEvents()
            
        elif command == 'SETCHANNEL':
            try:
                channel = data['value']
            except IndexError:
                log.error("No channel information")
            else:
                self.setchannel(channel)
                self.sendAvailableEvents()
                
        elif command == 'GETFILE':
            log.debug("want to download: %s" % data['value'])
            
            try:
                file_id = data['value']
                file_version = data['version']
            except IndexError:
                msg = {'action' : 'errormsg',
                    'value' : 'Missing ID'}
                self.transport.write( '%s\n' % json.dumps(msg) )
                return
                
            if (file_id, file_version) not in self.listEvents:
                msg = {'action' : 'errormsg',
                    'value' : 'File with id %s and version %s does not exist\n' % (file_id, file_version)}
                self.transport.write( '%s\n' % json.dumps(msg) )
                return
            
            log.debug( 'Sending file: %s' % self.listEvents[(file_id, file_version)] )
            
            msg = {'action' : 'filetransfert',
                'id' : file_id,
                'version': file_version,
                'hash' : self.listEvents[(file_id, file_version)]['hash']}
            self.transport.write( '%s\n' % json.dumps(msg) )
            self.setRawMode()
            
            for bytes in read_bytes_from_file(self.listEvents[(file_id, file_version)]['path']):
                self.transport.write(bytes)
            
            self.transport.write('\r\n')    
            self.setLineMode()
            
     
        # To remove
        elif command == 'quit':
            self.transport.loseConnection()
            
      

class TerminalFactory(protocol.ServerFactory):
    
    protocol = FileTransferProtocol
    
    def __init__(self, channelsList, videoCommunicator):        
        self.clients = []
        self.channelsList = channelsList
        self.videoCommunicator = videoCommunicator
        self.files = None
        
    
if __name__ == '__main__':
    parser = optparse.OptionParser()
    parser.add_option('-p', '--port', action = 'store', type = 'int', dest = 'port', default = 1234, help = 'server listening port')
    (options, args) = parser.parse_args()
    
    log.debug( 'Listening on port %d, serving files from directory: %s' % (options.port, ZIP_WIDGETS_FOLDER))

    reactor.listenTCP( options.port, FileTransferServerFactory() )
    reactor.run()